package kMeans.core;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @author vinicius
 *
 */
public class Cluster {
	public static final double THRESHOLD = 0.01;
	
	private double noveltyDistance = 0.0;
	private ArrayList<Double> clusterCenter = null;

	private int id = 0;

	private ArrayList<PadraoTreinamento> padroesAssociados = null;

	public Cluster(int pId, double pNoveltyDistance) {
		this.setId(pId);
		this.setNoveltyDistance(pNoveltyDistance);
	}

	public Cluster(int pId, double pNoveltyDistance, ArrayList<Double> pClusterCenter) {
		this.setId(pId);
		this.setNoveltyDistance(pNoveltyDistance);
		this.setClusterCenter(pClusterCenter);
	}
	
	public boolean adjustCenter() {
	
		double [] sumAllDimensions = null;
		
		ArrayList listaPadroes = this.getPadroesAssociados();
		
		try {
		//Dimensao do array que ira conter o valor da media de cada dimensao.
		sumAllDimensions = new double[((PadraoTreinamento)listaPadroes.get(0)).getValoresPadrao().size()];
		
		} catch (NullPointerException e) {
			return false;
		} catch (IndexOutOfBoundsException e) {
			return false;
		}
		//Para cada padrao, soma os valores e coloca em sumAllDimensions.
		for (int i = 0; i < listaPadroes.size(); i++) {
			PadraoTreinamento padraoTreinamento = (PadraoTreinamento) listaPadroes.get(i);
			ArrayList valoresPadrao = padraoTreinamento.getValoresPadrao();
			
			for (int k = 0; k < valoresPadrao.size(); k++) {
				sumAllDimensions[k] += ((Double) valoresPadrao.get(k)).doubleValue();
			}			
		}
		
		//Calcula a media de cada dimensao. O resultado devera ser usado para ajustar o centro do cluster.
		for (int i = 0; i < sumAllDimensions.length; i++) {
			sumAllDimensions[i] /= listaPadroes.size();
		}
		
		return this.compareCenterToMeans(sumAllDimensions);
	}
	
	/**
	 * @return
	 */
	public int getId() {
		return id;
	}

	/**
	 * @param pId
	 */
	public void setId(int pId) {
		id = pId;
	}

	/**
	 * @return noveltyDistance - valor a partir do qual um padrao eh considerado novidade para um cluster.
	 */
	public double getNoveltyDistance() {
		return noveltyDistance;
	}

	/**
	 * @param pNoveltyDistance
	 */
	public void setNoveltyDistance(double pNoveltyDistance) {
		noveltyDistance = pNoveltyDistance;
	}

	/**
	 * @return padroesAssociados - ArrayList<PadraoTreinamento> com os padroes atribuidos ao cluster.
	 */
	public ArrayList getPadroesAssociados() {
		return padroesAssociados;
	}

	/**
	 * @param pPadraoTreinamento
	 */
	public void incluirPadrao(PadraoTreinamento pPadraoTreinamento) {
		if (this.getPadroesAssociados() == null) {
			padroesAssociados = new ArrayList<PadraoTreinamento>();
		}
		padroesAssociados.add(pPadraoTreinamento);
	}

	/**
	 * @return
	 */
	public ArrayList<Double> getClusterCenter() {
		if (this.clusterCenter == null) {
			this.clusterCenter = new ArrayList<Double>();
		}
		return clusterCenter;
	}

	/**
	 * @param clusterCenter
	 */
	public void setClusterCenter(ArrayList<Double> clusterCenter) {
		this.clusterCenter = clusterCenter;
	}

	/**
	 * @param padroesAssociados
	 */
	public void setPadroesAssociados(ArrayList<PadraoTreinamento> padroesAssociados) {
		this.padroesAssociados = padroesAssociados;
	}

	/**
	 * @param pMeansArray
	 * @return
	 */
	private boolean compareCenterToMeans(double [] pMeansArray){		
		boolean houveAlteracao = false;
		ArrayList<Double> clusterCenter =  this.getClusterCenter();
		Iterator iteratorClusterCenter = clusterCenter.iterator();
		int i = 0;
		
		while (iteratorClusterCenter.hasNext()) {
			Double valorDimensaoCentro = (Double) iteratorClusterCenter.next();
			//Se o valor do centro nao estiver no intervalo de tolerancia ao comparar com as medias encontradas, adiciona o valor
			//daquela media para ser o novo centro para aquela dimensao.
			if (!(valorDimensaoCentro.doubleValue() <= pMeansArray[i] + THRESHOLD) || !(valorDimensaoCentro.doubleValue() >= pMeansArray[i] - THRESHOLD)) {
				this.clusterCenter.set(i, new Double(pMeansArray[i]));
				houveAlteracao = true;
			}
			i++;
		}
		
		return houveAlteracao;
	}
	
	public String toString() {
		return "Cluster[" + this.getId() + "]";
	}
}
